#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include "shm_com_sem.h"

// 先阅读producer代码，查看共享内存、信号量的使用说明

int main()
{
    void *shared_memory = (void *)0;
    struct shared_mem_st *shared_stuff;

    int shmid;
    int num_read;
    pid_t fork_result;
    sem_t *sem_queue, *sem_queue_empty, *sem_queue_full;
    // 获取共享内存区，并挂入内存
    if ((shmid = shmget(SHM_KEY, sizeof(struct shared_mem_st), 0666)) == -1)
    {
        perror("shmget fail!\n");
        exit(1);
    }
    if ((shared_memory = shmat(shmid, (void *)0, 0)) == (void *)-1)
    {
        perror("shmat fail!\n");
        exit(1);
    }

    // 将缓冲区指针转化为shared_mem_st类型
    shared_stuff = (struct shared_mem_st *)shared_memory;

    // 获取producer创建的3个信号量
    sem_queue = sem_open(queue_mutex, 0);
    if (sem_queue == SEM_FAILED)
    {
        perror("sem_open queue_mutex fail!\n");
        exit(EXIT_FAILURE);
    }
    sem_queue_empty = sem_open(queue_empty, 0);
    if (sem_queue_empty == SEM_FAILED)
    {
        perror("sem_open queue_empty fail!\n");
        exit(EXIT_FAILURE);
    }
    sem_queue_full = sem_open(queue_full, 0);
    if (sem_queue_full == SEM_FAILED)
    {
        perror("sem_open queue_full fail!\n");
        exit(EXIT_FAILURE);
    }

    shared_stuff->should_exit = 0;

    // 创建了两个进程
    fork_result = fork();
    if (fork_result == -1)
    {
        fprintf(stderr, "Fork failure\n");
    }
    if (fork_result == 0)
    { // 子进程
        while (1)
        {
            // 信号量操作，打印消费内容及进程号，发现 quit 退出
            // 首先检查退出标志
            sem_wait(sem_queue);
            if (shared_stuff->should_exit)
            {
                sem_post(sem_queue);
                break;
            }
            sem_post(sem_queue);

            // 信号量操作，打印消费内容及进程号，发现 quit 退出
            if (sem_trywait(sem_queue_full) == -1)
            {
                // 如果没有满的缓冲区，检查是否应该退出
                sem_wait(sem_queue);
                if (shared_stuff->should_exit)
                {
                    sem_post(sem_queue);
                    break;
                }
                sem_post(sem_queue);
                usleep(10000); // 短暂休眠后重试
                continue;
            }

            sem_wait(sem_queue); // 进入临界区

            if (!strcmp(shared_stuff->buffer[shared_stuff->line_read], "quit\n"))
            {
                // 设置退出标志
                shared_stuff->should_exit = 1;
                printf("Child process received quit signal\n");
                // 为父进程发送一个信号
                sem_post(sem_queue_full);
                sem_post(sem_queue);
                sem_post(sem_queue_empty);
                break;
            }

            printf("Consumer Child: %s", shared_stuff->buffer[shared_stuff->line_read]);
            shared_stuff->line_read = (shared_stuff->line_read + 1) % NUM_LINE;

            sem_post(sem_queue);       // 离开临界区
            sem_post(sem_queue_empty); // 发布空缓冲区信号量
        }
        // 释放信号量
        sem_close(sem_queue);
        sem_close(sem_queue_empty);
        sem_close(sem_queue_full);
    }
    else
    { // 父进程操作，与子进程相似
        while (1)
        {
            // 信号量操作，打印消费内容及进程号，发现 quit 退出
            // 首先检查退出标志
            sem_wait(sem_queue);
            if (shared_stuff->should_exit)
            {
                sem_post(sem_queue);
                break;
            }
            sem_post(sem_queue);

            // 信号量操作，打印消费内容及进程号，发现 quit 退出
            if (sem_trywait(sem_queue_full) == -1)
            {
                // 如果没有满的缓冲区，检查是否应该退出
                sem_wait(sem_queue);
                if (shared_stuff->should_exit)
                {
                    sem_post(sem_queue);
                    break;
                }
                sem_post(sem_queue);
                usleep(10000); // 短暂休眠后重试
                continue;
            }

            sem_wait(sem_queue); // 进入临界区

            if (!strcmp(shared_stuff->buffer[shared_stuff->line_read], "quit\n"))
            {
                // 设置退出标志
                shared_stuff->should_exit = 1;
                printf("Parent process received quit signal\n");
                // 为子进程发送一个信号
                sem_post(sem_queue_full);
                sem_post(sem_queue);
                sem_post(sem_queue_empty);
                break;
            }

            printf("Consumer Parent: %s", shared_stuff->buffer[shared_stuff->line_read]);
            shared_stuff->line_read = (shared_stuff->line_read + 1) % NUM_LINE;

            sem_post(sem_queue);       // 离开临界区
            sem_post(sem_queue_empty); // 发布空缓冲区信号量
        }
        // 释放信号量
        sem_close(sem_queue);
        sem_close(sem_queue_empty);
        sem_close(sem_queue_full);
    }
    exit(EXIT_SUCCESS);
}